package net.j4love.nio.channels;

import org.junit.Assert;
import org.junit.Test;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author he peng
 * @create 2018/5/31 14:11
 * @see
 */
public class FileChannelTest {

    @Test
    public void openTest() throws Exception {

        Path path = Paths.get("D:\\Documents\\Pictures\\test\\纸短情长.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ);
        System.out.println("open channel -> " + fChannel);
        if (fChannel.isOpen()) {
            System.out.println(path + " file is open");
        }
        fChannel.close();
    }

    @Test
    public void openTest1() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\" , "test" , "纸短情长.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ);
        System.out.println("open channel -> " + fChannel);
        if (fChannel.isOpen()) {
            System.out.println(path + " file is open");
        }
        fChannel.close();
    }

    @Test
    public void openTruncateExistingTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\" , "test" , "夜月一帘幽梦，春风十里柔情.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ , StandardOpenOption.TRUNCATE_EXISTING);
        System.out.println("open channel -> " + fChannel);
        if (fChannel.isOpen()) {
            System.out.println(path + " file is open");
        }
        fChannel.close();
    }

    @Test
    public void openDeleteOnCloseTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\" , "test" , "纸短情长.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ , StandardOpenOption.DELETE_ON_CLOSE);
        System.out.println("open channel -> " + fChannel);
        if (fChannel.isOpen()) {
            System.out.println(path + " file is open");
        }
        fChannel.close();
    }

    @Test
    public void readSingleBufferTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\纸短情长.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ);
        if (! fChannel.isOpen()) {
            System.out.println(path + " file not open");
        }

        // 将数据都读取到一个 buffer 中
        ByteBuffer dstBuffer = ByteBuffer.allocate(200);
        int readBytes;

        long startTime = System.currentTimeMillis();
        for (int i = 0 ; i < 900000 ; i++) {
            // byte[] 大小最好大于 buffer 的容量或者是等于 ， 如果小于的话处理起来会增加复杂度
            byte[] bufferBytes = null;
            int bytePos = 0;
            while ((readBytes = fChannel.read(dstBuffer)) != -1) {
                // 数据被读取到 buffer 中 ，再从 buffer 中读取数据
                if (bufferBytes == null) {
                    bufferBytes = new byte[readBytes];
                } else {
                    byte[] tmp = new byte[bufferBytes.length + readBytes];
                    System.arraycopy(bufferBytes , 0 , tmp , 0 , bufferBytes.length);
                    bufferBytes = tmp;
                }
                if (dstBuffer.hasArray()) {
                    System.arraycopy(dstBuffer.array() , 0 , bufferBytes , bytePos , readBytes);
                    bytePos += readBytes;
                } else {
                    throw new IllegalStateException("buffer Unsupported byte array");
                }
                dstBuffer.flip();
            }
//            System.out.println(new String(bufferBytes , 0 , (int)fChannel.size() , Charset.forName("utf-8")));
            // 为了能够重复通过通道读取文件
            fChannel.position(0);
        }

        fChannel.close();
        System.out.println(" time ====================> " + (System.currentTimeMillis() - startTime) + "ms");
        // n = 50000 , buffer = 20 bytes , time -> 704ms , 701ms , 742ms , 759ms , 745ms
        // n = 100000 , buffer = 20 bytes , time -> 1364ms , 1449ms , 1326ms , 1325ms , 1411ms , 1367ms

        // n = 100000 , buffer = 200 bytes , time -> 268ms , 279ms , 267ms , 275ms , 298ms ,  289ms , 265ms
    }

    @Test
    public void scatteringReadTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\纸短情长.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ);
        if (! fChannel.isOpen()) {
            System.out.println(path + " file not open");
        }
        System.out.println("channel file position -> " + fChannel.position());
        System.out.println("file size = " + fChannel.size());

        ByteBuffer buf0 = ByteBuffer.allocate(200);
        ByteBuffer buf1 = ByteBuffer.allocate(200);
        ByteBuffer buf2 = ByteBuffer.allocate(200);
        ByteBuffer[] buffers = {buf0 , buf1 , buf2};

        long startTime = System.currentTimeMillis();
        for (int q = 0 ; q < 900000 ; q++ ) {
            long readBytes;
            byte[] bufferBytes = null;
            int bytePos = 0;
            while ((readBytes = fChannel.read(buffers)) != -1) {
//                System.out.println("channel file position -> " + fChannel.position());
                if (bufferBytes == null) {
                    bufferBytes = new byte[(int)readBytes];
                } else {
                    byte[] tmp = new byte[(int)readBytes + bufferBytes.length];
                    System.arraycopy(bufferBytes , 0 , tmp , 0 , bufferBytes.length);
                    bufferBytes = tmp;
                }
                byte[] bytes = new byte[(int)readBytes];
                int previousBufferRemaining = 0;
                for (int i = 0 ; i < buffers.length ; i++ ) {
                    buffers[i].flip();
                    if (! buffers[i].hasRemaining()) {
                        continue;
                    }

                    int remaining = buffers[i].remaining();
                    buffers[i].get(bytes , previousBufferRemaining , remaining);
                    previousBufferRemaining += remaining;
                    buffers[i].clear();
                }
                System.arraycopy(bytes , 0 , bufferBytes , bytePos, (int)readBytes);
                bytePos += readBytes;
            }
//            System.out.println(new String(bufferBytes , Charset.forName("utf-8")));
            fChannel.position(0);
        }

        fChannel.close();
        System.out.println(" time ====================> " + (System.currentTimeMillis() - startTime) + "ms");
        // 堆内存缓冲区 n = 50000 , buffer = 20 bytes , time -> 812ms , 730ms , 748ms , 772ms , 757ms , 770ms
        // 直接内存缓冲区 n = 50000 , buffer = 20 bytes , time -> 730ms , 726ms , 690ms , 711ms , 679ms , 666ms

        // 堆内存缓冲区 n = 100000 , buffer = 20 bytes , time -> 1386ms ,  1430ms ，1438ms ，1404ms ，1503ms ， 1469ms
        // 直接内存缓冲区 n = 100000 , buffer = 20 bytes , time -> 1457ms , 1421ms , 1374ms , 1412ms , 1388ms , 1358ms

        // 堆内存缓冲区 n = 100000 , buffer = 200 bytes , time -> 298ms , 302ms , 305ms , 322ms , 283ms , 315ms , 312ms
        // 直接内存缓冲区 n = 100000 , buffer = 200 bytes , time -> 264ms ,  261ms , 274ms , 272ms , 261ms , 286ms

    }

    @Test
    public void createFileTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\临江仙·送钱穆父.txt");

        // create_new - Create a new file, failing if the file already exists.
        // create - Create a new file if it does not exist.
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.CREATE_NEW);
        Assert.assertTrue(fChannel.isOpen());

        System.out.println("file position -> " + fChannel.position());
        System.out.println("file size -> " + fChannel.size());

        fChannel.close();
    }

    @Test
    public void singleWriteTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\临江仙·送钱穆父.txt");

        // WRITE 打开文件的写权限
        // APPEND 从文件尾部开始写入 ， 而不是开头
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.CREATE_NEW , StandardOpenOption.WRITE, StandardOpenOption.APPEND);
        Assert.assertTrue(fChannel.isOpen());

        String s = "临江仙·送钱穆父\n" +
                "宋代：苏轼\n" +
                "一别都门三改火，天涯踏尽红尘。依然一笑作春温。无波真古井，有节是秋筠。\n" +
                "惆怅孤帆连夜发，送行淡月微云。尊前不用翠眉颦。人生如逆旅，我亦是行人。";

        byte[] bytes = s.getBytes(Charset.forName("utf-8"));
        ByteBuffer srcBuf = ByteBuffer.allocateDirect(bytes.length);
        srcBuf.put(bytes);
        srcBuf.flip();
        int size = fChannel.write(srcBuf);
        System.out.println("write size bytes -> " + size);
        System.out.println("file size -> " + fChannel.size());
        System.out.println("file position -> " + fChannel.position());

//        FileWriter fileWriter = new FileWriter("D:\\Documents\\Pictures\\test\\FileWriter.txt");
//        fileWriter.write(s);
//        fileWriter.flush();

        fChannel.close();
    }

    @Test
    public void gatheringWriteTest() throws Exception {

        // gathering 方式写入时会读取 buffer 中所有的数据
        // 所以要让 buffer 中每一个位置都有数据 (用有效数据将缓冲区填满) , 否则无法得到正确数据

        Path path = Paths.get("D:\\Documents\\Pictures\\test\\相见欢·林花谢了春红.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.CREATE_NEW , StandardOpenOption.WRITE, StandardOpenOption.APPEND);
        Assert.assertTrue(fChannel.isOpen());

        String s = "相见欢·林花谢了春红\n" +
                   "五代：李煜\n" +
                   "林花谢了春红，太匆匆。无奈朝来寒雨晚来风。\n" +
                   "胭脂泪，相留醉，几时重。自是人生长恨水长东。";
        byte[] bytes = s.getBytes(Charset.forName("utf-8"));
        List<ByteBuffer> buffers = new ArrayList<>(4);

        int splitNumber = 5;
        int offset = 0;
        for (int i = 0 ; i < splitNumber ; i++ ) {
            int size = bytes.length / splitNumber;
            ByteBuffer buffer = ByteBuffer.allocate(size);
            buffer.put(bytes , offset , size);
            offset += size;
            buffer.flip();
            buffers.add(buffer);
        }

        if (offset < bytes.length) {
            int surplus = bytes.length - offset;
            ByteBuffer buffer = ByteBuffer.allocate(surplus);
            buffer.put(bytes , offset , surplus);
            buffer.flip();
            buffers.add(buffer);
        }

        ByteBuffer[] bufferArray = new ByteBuffer[buffers.size()];
        bufferArray = buffers.toArray(bufferArray);

        long size = fChannel.write(bufferArray);
        System.out.println("write size -> " + size);

        fChannel.close();
    }

    @Test
    public void transferFromTest() throws Exception {
        Path path0 = Paths.get("D:\\Documents\\Pictures\\test\\And The Winner Is.txt");
        FileChannel fChannel0 = FileChannel.open(path0 , StandardOpenOption.CREATE , StandardOpenOption.WRITE , StandardOpenOption.READ);
        Assert.assertTrue(fChannel0.isOpen());

        String s = getAndTheWinnerIs();
        byte[] bytes = s.getBytes(Charset.forName("utf-8"));
        ByteBuffer buffer0 = ByteBuffer.allocateDirect(bytes.length);
        buffer0.put(bytes);
        buffer0.flip();
        int writeSize = fChannel0.write(buffer0);
        System.out.println("write size -> " + writeSize);

        Path path1 = Paths.get("D:\\Documents\\Pictures\\test\\And The Winner Is-Copy.txt");
        FileChannel fChannel1 = FileChannel.open(path1, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);

        // 在开始传输之前需要将源通道的 position 设置为 0 , 这样才能从 0 位置开始传输数据.
        fChannel0.position(0);
        long transferSize = fChannel1.transferFrom(fChannel0, 0, fChannel0.size());
        System.out.println("transfer size -> " + transferSize);

        fChannel1.close();
        fChannel0.close();
    }

    @Test
    public void transferToTest() throws Exception {
        Path path0 = Paths.get("D:\\Documents\\Pictures\\test\\And The Winner Is.txt");
        FileChannel fChannel0 = FileChannel.open(path0 , StandardOpenOption.CREATE , StandardOpenOption.WRITE , StandardOpenOption.READ);
        Assert.assertTrue(fChannel0.isOpen());

        String s = getAndTheWinnerIs();
        byte[] bytes = s.getBytes(Charset.forName("utf-8"));
        ByteBuffer buffer0 = ByteBuffer.allocateDirect(bytes.length);
        buffer0.put(bytes);
        buffer0.flip();
        int writeSize = fChannel0.write(buffer0);
        System.out.println("write size -> " + writeSize);

        Path path1 = Paths.get("D:\\Documents\\Pictures\\test\\And The Winner Is-Copy.txt");
        FileChannel fChannel1 = FileChannel.open(path1, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);

        // 在开始传输之前需要将源通道的 position 设置为 0 , 这样才能从 0 位置开始传输数据.
        long transferSize = fChannel0.transferTo(0, fChannel0.size(), fChannel1);
        System.out.println("transfer size -> " + transferSize);

        fChannel1.close();
        fChannel0.close();
    }

    @Test
    public void mapTest() throws Exception {

        // 在内存映射缓冲区上做的修改会同步到文件中
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\map test.txt");
        FileChannel fChannel = FileChannel.open(path , StandardOpenOption.CREATE_NEW , StandardOpenOption.WRITE , StandardOpenOption.READ);
        Assert.assertTrue(fChannel.isOpen());
        String s0 = "一声梧叶一声秋，一点芭蕉一点愁，三更归梦三更后。落灯花，棋未收，叹新丰逆旅淹留。枕上十年事，江南二老忧，都到心头。";
        byte[] bytes = s0.getBytes(Charset.forName("utf-8"));
        ByteBuffer buffer0 = ByteBuffer.allocateDirect(bytes.length);
        buffer0.put(bytes);
        buffer0.flip();
        fChannel.write(buffer0);

        String s1 = ">>>>>>>>>>>>>>>>>>";
        MappedByteBuffer buffer1 = fChannel.map(FileChannel.MapMode.READ_WRITE, 0, s1.getBytes().length);
        fChannel.close();

        buffer1.put(s1.getBytes(Charset.forName("utf-8")));

    }

    @Test
    public void jvm1LockTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\lock test.txt");
        FileChannel fChannel = FileChannel.open(path , StandardOpenOption.CREATE_NEW , StandardOpenOption.WRITE , StandardOpenOption.READ);
        Assert.assertTrue(fChannel.isOpen());

        // 尝试获取一个排他锁
        FileLock fileLock = fChannel.tryLock();
//        FileLock fileLock = fChannel.tryLock(0 , fChannel.size() , true);
        try {
            if (! fileLock.isValid()) {
                System.out.println("file lock is invalid");
                return;
            }
            String s0 = "一声梧叶一声秋，一点芭蕉一点愁，三更归梦三更后。落灯花，棋未收，叹新丰逆旅淹留。枕上十年事，江南二老忧，都到心头。";
            byte[] bytes = s0.getBytes(Charset.forName("utf-8"));
            ByteBuffer buffer0 = ByteBuffer.allocateDirect(bytes.length);
            buffer0.put(bytes);
            buffer0.flip();
            fChannel.write(buffer0);
            Thread.sleep(100000);
        } finally {
            fileLock.release();
            fChannel.close();
        }
    }

    @Test
    public void jvm2LockTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\lock test.txt");
        FileChannel fChannel = FileChannel.open(path , StandardOpenOption.WRITE , StandardOpenOption.READ);
        Assert.assertTrue(fChannel.isOpen());

        // 同一个文件在一个 channel 上被锁定后 ， 只有锁释放后其他 channel 才能操作此文件，否则会抛出异常
        // channel close , jvm 关闭 会导致文件锁被释放

        // 如果另一个锁定该文件的 channel 使用的不是共享锁而是排他锁 ， 这里会一直阻塞直到获取到锁
//        FileLock fileLock = fChannel.lock();

        // 不会阻塞立即返回，如果没有获取到锁返回 null
        FileLock fileLock = fChannel.lock(0 , fChannel.size() , true);
        try {
            if (Objects.isNull(fileLock) || ! fileLock.isValid()) {
                System.out.println("file lock is invalid");
                return;
            }
//            if (! fileLock.isShared()) {
//                System.out.println("file lock is not shared");
//                return;
//            }
            String s0 = "@@@@@@@@@@@@@@@@@@@@@@@<<<<M><><><><><><><>>>>>>>>>>>";
            byte[] bytes = s0.getBytes(Charset.forName("utf-8"));
            ByteBuffer buffer0 = ByteBuffer.allocateDirect(bytes.length);
            buffer0.put(bytes);
            buffer0.flip();
            fChannel.write(buffer0);
        } finally {
            if (Objects.nonNull(fileLock)) {
                fileLock.release();
            }
            fChannel.close();
        }
    }

    @Test
    public void MultiThreadingLockTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\lock test.txt");
        new Thread(() -> {

            FileChannel fChannel = null;
            FileLock fileLock = null;
            try {
                fChannel = FileChannel.open(path , StandardOpenOption.WRITE , StandardOpenOption.READ);
                Assert.assertTrue(fChannel.isOpen());

                // 获取一个排他锁
                fileLock = fChannel.lock();
                if (! fileLock.isValid()) {
                    System.out.println("file lock is invalid");
                    return;
                }
                System.out.println(Thread.currentThread().getName() + " Thread locking file");
                String s0 = "一声梧叶一声秋，一点芭蕉一点愁，三更归梦三更后。落灯花，棋未收，叹新丰逆旅淹留。枕上十年事，江南二老忧，都到心头。";
                byte[] bytes = s0.getBytes(Charset.forName("utf-8"));
                ByteBuffer buffer0 = ByteBuffer.allocateDirect(bytes.length);
                buffer0.put(bytes);
                buffer0.flip();
                fChannel.write(buffer0);
                Thread.sleep(100000);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (Objects.nonNull(fileLock)) {
                    try {
                        fileLock.release();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (Objects.nonNull(fChannel)) {
                    try {
                        fChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "FileLockThread-1").start();

        new Thread(() -> {

            FileChannel fChannel = null;
            FileLock fileLock = null;
            try {
                fChannel = FileChannel.open(path , StandardOpenOption.WRITE , StandardOpenOption.READ);
                Assert.assertTrue(fChannel.isOpen());

                fileLock = fChannel.lock();
                if (Objects.isNull(fileLock) || ! fileLock.isValid()) {
                    System.out.println("file lock is invalid");
                    return;
                }
                System.out.println(Thread.currentThread().getName() + " Thread locking file");
                String s0 = "@@@@@@@@@@@@@@@@@@@@@@@<<<<M><><><><><><><>>>>>>>>>>>";
                byte[] bytes = s0.getBytes(Charset.forName("utf-8"));
                ByteBuffer buffer0 = ByteBuffer.allocateDirect(bytes.length);
                buffer0.put(bytes);
                buffer0.flip();
                fChannel.write(buffer0);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (Objects.nonNull(fileLock)) {
                    try {
                        fileLock.release();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (Objects.nonNull(fChannel)) {
                    try {
                        fChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } , "FileLockThread-2").start();

        Thread.sleep(10000);
    }

    @Test
    public void truncateTest() throws Exception {
        Path path = Paths.get("D:\\Documents\\Pictures\\test\\夜月一帘幽梦，春风十里柔情.txt");
        FileChannel fChannel = FileChannel.open(path, StandardOpenOption.CREATE_NEW , StandardOpenOption.WRITE, StandardOpenOption.APPEND);
        Assert.assertTrue(fChannel.isOpen());

        String s = "夜月一帘幽梦，春风十里柔情\n" +
                    "出自宋代秦观的《八六子·倚危亭》\n" +
                    "倚危亭。恨如芳草，萋萋刬尽还生。念柳外青骢别后，水边红袂分时，怆然暗惊。\n" +
                    "无端天与娉婷。夜月一帘幽梦，春风十里柔情。\n" +
                    "怎奈向、欢娱渐随流水，素弦声断，翠绡香减，那堪片片飞花弄晚，蒙蒙残雨笼晴。正销凝。黄鹂又啼数声。";
        byte[] bytes = s.getBytes(Charset.forName("utf-8"));
        ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
        buffer.put(bytes);
        buffer.flip();

        int size = fChannel.write(buffer);

        System.out.println(fChannel.position());
        // 将文件阶段为指定的长度
//        fChannel.position("夜月一帘幽梦，春风十里柔情".getBytes(Charset.forName("utf-8")).length);
        fChannel.truncate(bytes.length / 2);
        System.out.println("write size -> " + size);

        fChannel.close();
    }

    String getAndTheWinnerIs() {
        return "And The Winner Is\n" +
                "作曲 : Lavoine, Palmada\n" +
                "歌手：Gérard Darmon\n" +
                "Je voudrais remercier ma mère qui m'a nourri, qui m'a puni\n" +
                "我要感谢我的母亲，感谢她教养我\n" +
                "Je voudrais remercier Molière qui n'a jamais reçu son prix\n" +
                "我要感谢莫里哀，他没得到这个奖项\n" +
                "Dans cette cérémonie étrange où je suis nominé à vie\n" +
                "我却在这陌生仪式上获得了终身提名\n" +
                "Je suis ému, tout se mélange, je me lève et je vous souris\n" +
                "我激动万分，百感交集，我起身，向你们致以微笑\n" +
                "And the winner is : la vie, and the winner is : l'amour\n" +
                "得奖者是：生活，得奖者是：爱情\n" +
                "Je voudrais remercier ma mort pour faire preuve d'autant de patience\n" +
                "我想感谢死神，感谢她如此有耐心\n" +
                "Façon de conjurer le sort d'avoir peur avec élégance\n" +
                "犹如施展了魔法，让人害怕却不失优雅\n" +
                "Je voudrais remercier l'ami qui sait mieux que moi qui je suis\n" +
                "我想感谢我的朋友，他比我自己还了解我\n" +
                "Je l'embrasse et je lui dédie mes cuites et mon enfance aussi\n" +
                "向他问好，喝醉也好，童年也好，多亏有他\n" +
                "And the winner is : la vie, and the winner is : l'amour\n" +
                "得奖者是：生活，得奖者是：爱情\n" +
                "Je voudrais remercier les femmes et la mienne en particulier\n" +
                "我想感谢所有的女性，特别是我的妻子\n" +
                "Tant de bonheur et quelques drames mais je ne suis que leur moitié\n" +
                "满满的幸福，夹杂着些许悲伤，但我只占一半\n" +
                "Un clin d'œi à mes ennemis qui me font la gueule aujourd'hui\n" +
                "瞄一眼我的敌人们，今天还对我破口大骂\n" +
                "Sans eux, je crois que je m'ennuie, alors je vais leur dire merci\n" +
                "没有他们，我想我会很无聊，所以我要对他们说谢谢\n" +
                "And the winner is : la vie, and the winner is : l'amour\n" +
                "得奖者是：生活，得奖者是：爱情\n" +
                "Je porte la main sur le cœur et je vous salue encore une fois\n" +
                "以手抚胸，再次向你们致意\n" +
                "Je garde le sourire et mes larmes, je les garde pour moi\n" +
                "我收藏下这份喜悦与泪水，为自己收藏\n" +
                "And the winner is : la vie, and the winner is : l'amour\n" +
                "得奖者是：生活，得奖者是：爱情\n" +
                "And the winner is : la vie, and the winner is : l'amour.\n" +
                "得奖者是：生活，得奖者是：爱情\n" +
                "And the winner is : la vie, and the winner is : l'amour.\n" +
                "得奖者是：生活，得奖者是：爱情\n" +
                "And the winner is : la vie, and the winner is : l'amour.\n" +
                "得奖者是：生活，得奖者是：爱情\n" +
                "And the winner is : la vie, and the winner is : l'amour.\n" +
                "得奖者是：生活，得奖者是：爱情";
    }
}
